package com.come4loves.game.server.session;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import com.come4loves.core.comunicate.protocol.Response;
import com.come4loves.core.result.TResult;
import com.come4loves.core.utils.StringUtils;
import com.come4loves.core.utils.TimeUtils;
import com.come4loves.game.component.listener.ListenerFacade;
import com.come4loves.game.constent.GameStatusCodeConstant;
import com.come4loves.game.server.firewall.FloodRecord;

@Component
public class PlayerContext implements ChannelHandlerContextManager {
	private static final Logger LOGGER = LoggerFactory.getLogger(PlayerContext.class);
	public static final AttributeKey<Integer> CONTEXT_INDEX_KEY = AttributeKey.valueOf("contextIndexKey");
	public static final AttributeKey<Long> CONTEXT_ACTOR_ID = AttributeKey.valueOf("contextActorId");
	private Map<Integer, ChannelHandlerContext> ANONYMOUS_MAP = new ConcurrentHashMap<>();
	private Map<Long, ChannelHandlerContext> ACTOR_MAP = new ConcurrentHashMap<>();
	private static final Map<String, UserReconnectData> USER_LOGIN_DATA = new com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap.Builder<String, UserReconnectData>()
			.maximumWeightedCapacity(Short.MAX_VALUE).build();
	@Autowired
	private ListenerFacade listenerFacade;

	@Autowired
	@Qualifier("game.server.version")
	private String version;

	public void put2OnlineList(long actorId, ChannelHandlerContext context) {
		context.attr(CONTEXT_ACTOR_ID).set(actorId);
		ACTOR_MAP.put(actorId, context);
		listenerFacade.addLoginListener(actorId);
	}

	public void removeOnlineList(ChannelHandlerContext context) {
		if (context.attr(CONTEXT_ACTOR_ID).get() != null) {
			long actorId = context.attr(CONTEXT_ACTOR_ID).get();
			ACTOR_MAP.remove(actorId);
		}
	}

	public void removeOnlineList(long actorId) {
		ACTOR_MAP.remove(actorId);
		if (actorId > 0) {
			listenerFacade.addLogoutListener(actorId);
		}
	}

	public void put2AnonymousList(ChannelHandlerContext context) {
		int contextId = context.attr(CONTEXT_INDEX_KEY).get();
		ANONYMOUS_MAP.put(contextId, context);
	}

	public void removeFromAnonymousList(ChannelHandlerContext context) {
		int contextId = context.attr(CONTEXT_INDEX_KEY).get();
		ANONYMOUS_MAP.remove(contextId);
	}

	/**
	 * 角色是否在线
	 * 
	 * @param actorId
	 * @return
	 */
	public boolean isOnline(long actorId) {
		return ACTOR_MAP.containsKey(actorId);
	}

	public boolean isOnline(ChannelHandlerContext context) {
		long actorId = getActorId(context);
		if (actorId < 1L) {
			return false;
		}

		return ACTOR_MAP.containsKey(actorId);
	}

	/**
	 * 获取actorId
	 * 
	 * @param session
	 *            当前用户session对象
	 * @return
	 */
	public Long getActorId(ChannelHandlerContext context) {
		Long actorId = null;
		if (context != null) {
			actorId = context.attr(CONTEXT_ACTOR_ID).get();
		}
		return actorId == null ? 0L : actorId;
	}

	/**
	 * 获取在线玩家playerId列表
	 * 
	 * @return
	 */
	public Set<Long> onlineActorList() {
		return ACTOR_MAP.keySet();
	}

	/**
	 * 获取当前玩家在线人数
	 * 
	 * @return
	 */
	public int onlineActorNum() {
		return ACTOR_MAP.size();
	}

	public void closeChannelHandlerContext(ChannelHandlerContext context) {
		if (context != null) {
			context.close();
		}
	}

	/**
	 * 获取在线玩家的IoSession
	 * 
	 * @param actorId
	 * @return
	 */
	public ChannelHandlerContext getOnlineContext(long actorId) {
		return ACTOR_MAP.get(actorId);
	}

	public void push(long actorId, Response response) {
		ChannelHandlerContext session = getOnlineContext(actorId);
		push(session, response);
	}

	public ChannelFuture push(final ChannelHandlerContext session, final Response response) {
		if (session == null) {
			return null;
		}

		if (response != null) {
			ChannelFuture f = session.writeAndFlush(response);
			if (LOGGER.isDebugEnabled()) {
				f.addListener(new ChannelFutureListener() {

					@Override
					public void operationComplete(ChannelFuture arg0) throws Exception {

						byte module = response.getModule();
						byte cmd = response.getCmd();
						int byteLength = 0;
						if (response.getValue() != null) {
							byteLength = response.getValue().length;
						}
						Long actorId = getActorId(session);
						LOGGER.debug(String.format("[messageSent] sessionId:[%s] actorId:[%s] module:[%s] cmd:[%s] statuscode:[%s], length:[%d byte]", getAtomicId(session), actorId, module, cmd,
								response.getStatusCode(), byteLength));
					}
				});
			}
			return f;
		}

		return null;
	}

	public ChannelFuture pushAndClose(final ChannelHandlerContext session, final Response response) {
		if (session == null) {
			return null;
		}

		if (response != null) {
			ChannelFuture f = session.writeAndFlush(response);
			if (LOGGER.isDebugEnabled()) {
				f.addListener(new ChannelFutureListener() {

					@Override
					public void operationComplete(ChannelFuture arg0) throws Exception {

						byte module = response.getModule();
						byte cmd = response.getCmd();
						int byteLength = 0;
						if (response.getValue() != null) {
							byteLength = response.getValue().length;
						}
						Long actorId = getActorId(session);
						LOGGER.debug(String.format("[messageSent] sessionId:[%s] actorId:[%s] module:[%s] cmd:[%s] statuscode:[%s], length:[%d byte]", getAtomicId(session), actorId, module, cmd,
								response.getStatusCode(), byteLength));
					}
				});
			}
			f.addListener(ChannelFutureListener.CLOSE);
			return f;
		}

		return null;
	}

	public void setUserLogin(ChannelHandlerContext session, String platformType, String uid) {
		if (platformType != null && !(platformType.isEmpty()) && uid != null && uid.isEmpty() == false) {
			session.attr(SessionKey.PLATFORM_TYPE).set(platformType);
			session.attr(SessionKey.PLATFORM_UID).set(uid);

		}
	}

	public String setLoginData(String uid) {
		UserReconnectData userLoginData = new UserReconnectData(uid);
		USER_LOGIN_DATA.put(userLoginData.getReconnectId(), userLoginData);
		return userLoginData.getReconnectId();
	}

	public boolean userIsLogin(ChannelHandlerContext session) {
		String uid = getUid(session);
		if (uid == null || uid.isEmpty()) {
			return false;
		}

		return true;
	}

	/**
	 * 获取token
	 * 
	 * @param session
	 * @return
	 */
	public String getUid(ChannelHandlerContext session) {
		String uid = session.attr(SessionKey.PLATFORM_UID).get();
		if (uid == null) {
			return "";
		}
		return uid;
	}

	public void setServerId(ChannelHandlerContext session, int serverId) {
		session.attr(SessionKey.SERVER_ID).set(serverId);
	}

	public String getRemoteIp(ChannelHandlerContext session) {
		if (session == null) {
			return "";
		}

		String remoteIp = session.attr(SessionKey.REMOTE_HOST).get();
		if (StringUtils.isBlank(remoteIp) == false) {
			return remoteIp;
		}

		SocketAddress add = session.channel().remoteAddress();
		if (add != null) {
			remoteIp = ((InetSocketAddress) add).getAddress().getHostAddress();
		}
		if (StringUtils.isBlank(remoteIp)) {
			remoteIp = ((InetSocketAddress) session.channel().localAddress()).getAddress().getHostAddress();
		}
		session.attr(SessionKey.REMOTE_HOST).set(remoteIp);
		;

		return remoteIp;
	}

	public String getRemoteIp(long actorId) {

		ChannelHandlerContext ctx = getOnlineContext(actorId);
		if (ctx == null) {
			return "";
		}

		return getRemoteIp(ctx);
	}

	public TResult<UserReconnectData> validateReconnect(String reconnectId, String version) {
		if (this.version.equals(version) == false) {
			return TResult.valueOf(GameStatusCodeConstant.SERVER_VERSION_ERROR);
		}
		if (StringUtils.isBlank(reconnectId) || !USER_LOGIN_DATA.containsKey(reconnectId)) {
			return TResult.valueOf(GameStatusCodeConstant.USER_RECONNECT_ID_ERROR);
		}
		UserReconnectData userLoginData = USER_LOGIN_DATA.get(reconnectId);
		if (userLoginData.isTimeOut()) {
			USER_LOGIN_DATA.remove(reconnectId);
			return TResult.valueOf(GameStatusCodeConstant.USER_RECONNECT_TIME_OUT);
		}

		if (userLoginData.getReconnectId().equals(reconnectId) == false) {
			return TResult.valueOf(GameStatusCodeConstant.USER_RECONNECT_ID_ERROR);
		}
		userLoginData.setTime(TimeUtils.getNow());
		return TResult.sucess(userLoginData);
	}

	@Override
	public void close(ChannelHandlerContext ctx) {
		removeFromAnonymousList(ctx);
		removeOnlineList(ctx);
		ctx.close();
	}

	/**
	 * 获取洪水记录对象
	 * 
	 * @param session
	 * @return
	 */
	public FloodRecord getFloodRecord(ChannelHandlerContext session) {
		return session.attr(SessionKey.FLOOD_RECORD).get();
	}

	/**
	 * 设置洪水记录对象
	 * 
	 * @param session
	 * @param floodRecord
	 */
	public void setFloodRecord(ChannelHandlerContext session, FloodRecord floodRecord) {
		session.attr(SessionKey.FLOOD_RECORD).set(floodRecord);
	}

	/**
	 * 推送消息给用户列表
	 * 
	 * @param actorIdList
	 * @param response
	 */
	public void push(Collection<Long> actorIdList, Response response) {
		if ((actorIdList == null) || (actorIdList.isEmpty())) {
			return;
		}

		for (Long actorId : actorIdList) {
			ChannelHandlerContext session = getOnlineContext(actorId);
			if (session != null) {
				push(session, response);
			}
		}
	}

	public Integer getAtomicId(ChannelHandlerContext ctx) {
		return ctx.attr(ChannelHandlerContextManager.CONTEXT_INDEX_KEY).get();
	}

}
